Skip to main content

1. Creating new type

OOP elements fit Wolfram Language very nicely if you thing about in more general sense as an abstract isolated entities of something which can communicate with each other using messages and have their own internal states.

Properties and methods

Let it be an object with a single field default "State"

CreateType[StateMachine, init, {"State"->1}]
init[o_] := o["UId"] = CreateUUID[];

Here we also assigned a unique text ID to it for the future using constructor function init. By calling now StateMachine we create an instance of this type

machine = StateMachine[]

Think about it if it as an Association.

One can assign any properties, but just be aware that Set has HoldFirst attribute and set any properties using With

With[{o = machine},
o["State"] = 33
]

One can also see all properties using a special key

machine["Properties"]
info

See the full guide from Kirill Belov on Objects core package at Wolfam Community

note

There is no need in installing Objects paclet. This is already a part of WLJS Notebook Kernel

Time to define sort of methods of created types. It is based solely on TagSet technique widely used in Wolfram Language

StateMachine /: StateMachineChange[s_StateMachine, state_Integer] := With[{},
s["State"] = state;
s
]

StateMachine /: Delete[s_StateMachine] := With[{},
DeleteObject[s]
]

then to update the state we only need to call this on our object instance

StateMachineChange[machine, 1];
machine["State"]
1

In a case if you want an experience close to traditional OOP, one can utilize contexts

StateMachine /: StateMachine`Change[s_StateMachine, state_Integer] := With[{},
s["State"] = state;
s
]
StateMachine`Change[machine, 1];
machine["State"]

Event system

Subscription / published model is quite often applied to objects in OOP. Moreover this comes handy when we need to perform asynchronous tasks and needles to say it plays a great role in communicating between different objects. Here we will integrate it with Events system we built.

Firstly, since Events uses mostly text-strings as an identifiers for event objects, we rely on "UId" field generated in a constructor function

StateMachine /: EventFire[s_StateMachine, opts__] := EventFire[s["UId"], opts]

StateMachine /: EventHandler[s_StateMachine, opts__] := EventHandler[s["UId"], opts]

StateMachine /: EventClone[s_StateMachine] := EventClone[s["UId"]]
StateMachine /: EventRemove[s_StateMachine] := EventRemove[s["UId"]]

To notify all subscribers we need to modify our method of settings the state

StateMachine /: StateMachineChange[s_StateMachine, state_Integer] := With[{},
s["State"] = state;
EventFire[s, "State", state]; (* THIS LINE *)
s
]

StateMachine /: Delete[s_StateMachine] := With[{},
EventFire[s, "Destroy", Null];
DeleteObject[s]
]

Let us check how it works!

machine = StateMachine[];
EventHandler[StateMachine, Beep]; (* make sound *)
StateMachineChange[machine, RandomInteger[{1,10}]];

Every time you change the state it will make sound.

info

See more about event system here

One can also subscribe to a particular pattern or topic

EventHandler[StateMachine, {
"State" -> Function[state, Print[state]]
}];
info

This is a core mechanism in WLJS notebook interface